package com.ruoyi.web.controller.system;

import cc.renzhihao.sso.bean.JwtClient;
import cc.renzhihao.sso.config.IamConfig;
import cc.renzhihao.sso.service.ProfileManagerService;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.net.url.UrlQuery;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.*;
import com.ruoyi.framework.shiro.auth.CustomizedToken;
import com.ruoyi.framework.shiro.enums.LoginType;
import com.ruoyi.framework.shiro.service.SysPasswordService;
import com.ruoyi.system.domain.Pac4jProfile;
import com.ruoyi.system.domain.SysAuth;
import com.ruoyi.system.service.ISysAuthService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysMenuService;
import org.apache.commons.compress.utils.Lists;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.pac4j.cas.client.CasClient;
import org.pac4j.core.config.Config;
import org.pac4j.core.profile.ProfileManager;
import org.pac4j.core.profile.UserProfile;
import org.pac4j.oauth.client.GenericOAuth20Client;
import org.pac4j.oauth.client.OAuth20Client;
import org.pac4j.oidc.client.OidcClient;
import org.pac4j.saml.client.SAML2Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 首页 业务处理
 *
 * @author ruoyi
 */
@Controller
public class SysIndexController extends BaseController {
    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private SysPasswordService passwordService;

    @Autowired
    private RuoYiConfig ruoYiConfig;

    @Autowired
    private ProfileManagerService profileManagerService;

    @Autowired
    private IamConfig iamConfig;

    @Autowired
    private ProfileManager profileManager;

    @Autowired
    private ISysAuthService authService;

    @Autowired
    private Config config;

    // 系统首页
    @GetMapping("/index")
    public String index(ModelMap mmap) {
        // 取身份信息
        SysUser user = getSysUser();

        String centralLogoutUrl = iamConfig.getCentralLogout().getLogoutPath();
        String nativeLogoutUrl = iamConfig.getLocalLogout().getLogoutPath();
        UserProfile profile = profileManagerService.getProfile();

        if (profile != null) {
            String clientName = profile.getClientName();
            SysAuth sysAuth = authService.getAuthWayByClientName(clientName);

            UrlQuery urlQuery = new UrlQuery();
            urlQuery.add("client_name", clientName);
            UrlBuilder urlBuilder = UrlBuilder.ofHttp(sysAuth.getLocalServer())
                    .addPath(sysAuth.getCallbackUrl())
                    .setQuery(urlQuery);

//            UrlQuery query = new UrlQuery();
//            query.add("service", urlBuilder.toString());
//            UrlBuilder url = UrlBuilder.ofHttp(sysAuth.getLogoutUrl())
//                    .setQuery(query);

            String serviceUrl = urlBuilder.toString();
            centralLogoutUrl += "?url=" + URLEncoder.encode(serviceUrl, StandardCharsets.UTF_8);
        }
        mmap.put("centralLogoutUrl", centralLogoutUrl);
        mmap.put("nativeLogoutUrl", nativeLogoutUrl);

        // 根据用户id取出菜单
        List<SysMenu> menus = menuService.selectMenusByUser(user);
        mmap.put("menus", menus);
        mmap.put("user", user);
        mmap.put("sideTheme", configService.selectConfigByKey("sys.index.sideTheme"));
        mmap.put("skinName", configService.selectConfigByKey("sys.index.skinName"));
        Boolean footer = Convert.toBool(configService.selectConfigByKey("sys.index.footer"), true);
        Boolean tagsView = Convert.toBool(configService.selectConfigByKey("sys.index.tagsView"), true);
        mmap.put("footer", footer);
        mmap.put("tagsView", tagsView);
        mmap.put("mainClass", contentMainClass(footer, tagsView));
        mmap.put("copyrightYear", RuoYiConfig.getCopyrightYear());
        mmap.put("demoEnabled", RuoYiConfig.isDemoEnabled());
        mmap.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
        mmap.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
        mmap.put("isMobile", ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")));

        // 菜单导航显示风格
        String menuStyle = configService.selectConfigByKey("sys.index.menuStyle");
        // 移动端，默认使左侧导航菜单，否则取默认配置
        String indexStyle = ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")) ? "index" : menuStyle;

        // 优先Cookie配置导航菜单
        Cookie[] cookies = ServletUtils.getRequest().getCookies();
        for (Cookie cookie : cookies) {
            if (StringUtils.isNotEmpty(cookie.getName()) && "nav-style".equalsIgnoreCase(cookie.getName())) {
                indexStyle = cookie.getValue();
                break;
            }
        }
        String webIndex = "topnav".equalsIgnoreCase(indexStyle) ? "index-topnav" : "index";
        return webIndex;
    }

    @GetMapping("/centralLogin")
    public String centralLogin(ModelMap mmap) {
        UserProfile profile = profileManagerService.getProfile();

        if (profile != null) {
            CustomizedToken token = new CustomizedToken(profile, LoginType.SSO);
            Subject subject = SecurityUtils.getSubject();
            try {
                subject.login(token);
            } catch (AuthenticationException e) {
                logger.error("SSO认证失败", e);
                return "login";
            }
        }

        // 取身份信息
        SysUser user = getSysUser();
        if (user == null) {
            return "redirect:login";
        }

        return "redirect:index";
    }

    @PostMapping("profile/list")
    @ResponseBody
    public TableDataInfo profileList() {
        SysUser sysUser = ShiroUtils.getSysUser();

        UserProfile profile = profileManagerService.getProfile();
        TableDataInfo tableDataInfo = new TableDataInfo();

        if (profile != null) {
            tableDataInfo.setCode(0);
            tableDataInfo.setMsg("成功");
        } else {
            List<Pac4jProfile> list = Lists.newArrayList();
            tableDataInfo.setCode(0);
            if (sysUser != null && Objects.nonNull(sysUser.getThirdProfile())) {
                tableDataInfo.setMsg("第三方认证");
                sysUser.getThirdProfile().forEach((k, v) -> {
                    if (v == null) {
                        return;
                    }
                    Pac4jProfile pac4jProfile = new Pac4jProfile(k, v, v.getClass().toString());
                    list.add(pac4jProfile);
                });
                tableDataInfo.setRows(list);
                tableDataInfo.setTotal(list.size());
                return tableDataInfo;
            }

            tableDataInfo.setMsg("账密登录");
            tableDataInfo.setRows(Lists.newArrayList());
            tableDataInfo.setTotal(0);
            return tableDataInfo;
        }

        String account = profile.getId();
        String authWay = getAuthWay();
        String sessionId = profileManagerService.getSessionId();

        Map<String, Object> attributes = profile.getAttributes();

        List<Pac4jProfile> list = new ArrayList<>();
        list.add(new Pac4jProfile("认证账号", account, String.class.toString()));
        list.add(new Pac4jProfile("认证方式", authWay, String.class.toString()));
        list.add(new Pac4jProfile("会话ID", sessionId, String.class.toString()));
        list.add(new Pac4jProfile("clientName", profile.getClientName(), String.class.toString()));

        attributes.forEach((k, v) -> {
            Pac4jProfile pac4jProfile = new Pac4jProfile(k, v, v.getClass().toString());
            list.add(pac4jProfile);
        });

        tableDataInfo.setRows(list);
        tableDataInfo.setTotal(list.size());

        return tableDataInfo;
    }

    // 锁定屏幕
    @GetMapping("/lockscreen")
    public String lockscreen(ModelMap mmap) {
        mmap.put("user", getSysUser());
        ServletUtils.getSession().setAttribute(ShiroConstants.LOCK_SCREEN, true);
        return "lock";
    }

    // 解锁屏幕
    @PostMapping("/unlockscreen")
    @ResponseBody
    public AjaxResult unlockscreen(String password) {
        SysUser user = getSysUser();
        if (StringUtils.isNull(user)) {
            return AjaxResult.error("服务器超时，请重新登录");
        }
        if (passwordService.matches(user, password)) {
            ServletUtils.getSession().removeAttribute(ShiroConstants.LOCK_SCREEN);
            return AjaxResult.success();
        }
        return AjaxResult.error("密码不正确，请重新输入。");
    }

    // 切换主题
    @GetMapping("/system/switchSkin")
    public String switchSkin() {
        return "skin";
    }

    // 切换菜单
    @GetMapping("/system/menuStyle/{style}")
    public void menuStyle(@PathVariable String style, HttpServletResponse response) {
        CookieUtils.setCookie(response, "nav-style", style);
    }

    // 系统介绍
    @GetMapping("/system/main")
    public String main(ModelMap mmap) {
        mmap.put("version", RuoYiConfig.getVersion());
        return "main";
    }

    // content-main class
    public String contentMainClass(Boolean footer, Boolean tagsView) {
        if (!footer && !tagsView) {
            return "tagsview-footer-hide";
        } else if (!footer) {
            return "footer-hide";
        } else if (!tagsView) {
            return "tagsview-hide";
        }
        return StringUtils.EMPTY;
    }

    // 检查初始密码是否提醒修改
    public boolean initPasswordIsModify(Date pwdUpdateDate) {
        Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
        return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
    }

    // 检查密码是否过期
    public boolean passwordIsExpiration(Date pwdUpdateDate) {
        Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
        if (passwordValidateDays != null && passwordValidateDays > 0) {
            if (StringUtils.isNull(pwdUpdateDate)) {
                // 如果从未修改过初始密码，直接提醒过期
                return true;
            }
            Date nowDate = DateUtils.getNowDate();
            return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
        }
        return false;
    }

    public String getAuthWay() {
        return profileManager.getProfile().map(profile -> config.getClients().findClient(profile.getClientName()).map(client -> {
            if (client instanceof GenericOAuth20Client) {
                return "GENERIC_OAUTH2";
            } else if (client instanceof CasClient) {
                return "CAS";
            } else if (client instanceof OidcClient) {
                return "OIDC";
            } else if (client instanceof OAuth20Client) {
                return "OAUTH2";
            } else if (client instanceof JwtClient) {
                return "JWT";
            } else if (client instanceof SAML2Client) {
                return "SAML2";
            } else {
                return "未知";
            }
        }).orElse("未知")).orElse("未知");
    }
}
